/* Susan(tm) Java Version
   by Thomas Lochmatter, Brig-Glis, Switzerland
   and Patrick Bizeau, Brig-Glis, Switzerland    */

package laWa;
import modelnew.Computer;
import modelnew.Field;

// The Game itself. Manages the field.field an all operation within it.
// Also contains the computer strategies.
public class SusanGame {

  // ######################### INSTANCE VARIABLES

  // Who's turn is it?
  public int playturn;

  // Status of the game
  int status=0;
  public static final int statusstopped=0;
  public static final int statusplaying=1;
  public static final int statusfinished=2;

  // The stone already chosen, this is used to perform drags
  // if this is equal to the Field.magicfield, no stone is chosen
  public int dragfrom=Field.magicfield;

  // Count the drags, because of the rule of the three drags
  public int dragcount=0;

  // The handler of SusanGame events
  SusanGameListener eventhandler=null;
  
  private Computer computer;
  private Field field;

  // ######################### CONSTRUCTOR
  SusanGame(int beginplayturn, SusanGameListener sgl) {
    status=statusplaying;
    playturn=beginplayturn;

    if (sgl==null) {
      eventhandler = new SusanGameMultiListener();
    } else {
      eventhandler = sgl;
    }
    
    field = new Field();
    computer = new Computer(getField(), this);
    
  }

  // ######################### BASIC GAME FUNCTIONALITY
  // These methods are needed to play Susan.

  // Performs a drag if possible
  public boolean Drag(int from, int to) {
    if (!canDrag(from,to)) {return false;}
    getField().field[to]=getField().field[from];
    getField().field[from] ^= Field.fieldoccupied;
    dragcount++;
    eventhandler.SusanGameDragPerformed(playturn,from,to);
    FinishAction();
    return true;
  }

  // Performs a drag with the already set dragfrom variable
  public boolean Drag(int to) {
    return Drag(dragfrom,to);
  }

  // Checks if a drag is possible
  public boolean canDrag(int from, int to) {
    if (status!=statusplaying) {return false;}
    if ((getField().field[from] & Field.fieldexoccupied)!=Field.fieldexoccupiedyes) {return false;}
    if ((getField().field[from] & Field.fieldplayer)!=playturn) {return false;}
    if ((getField().field[to] & Field.fieldexoccupied)!=Field.fieldexoccupiedno) {return false;}
    int diff=from-to;
    for(int i=0; i<6; i++) {
      if (Field.around[i]==diff) {return true;}
    }
    return false;
  }

  // Checks if a drag with this stone would be possible
  public boolean canDrag(int from) {
    if (status!=statusplaying) {return false;}
    if ((getField().field[from] & Field.fieldexoccupied)!=Field.fieldexoccupiedyes) {return false;}
    if ((getField().field[from] & Field.fieldplayer)!=playturn) {return false;}
    for(int i=0; i<6; i++) {
      if (((getField().field[from+Field.around[i]] & Field.fieldoccupied)==0) && ((getField().field[from+Field.around[i]] & Field.fieldoccupied)==0)) {return true;}
    }
    return false;
  }

  // Marks the possible drags Field.around a field.field or deletes the marks
  public boolean markDrag(int from) {
    boolean changed=false;
    for(int i=0; i<6; i++){
      if ((getField().field[dragfrom+Field.around[i]] & Field.fieldmarkdrag)!=0) {getField().field[dragfrom+Field.around[i]] ^= Field.fieldmarkdrag; changed=true;}
    }
    if (dragfrom==from) {dragfrom=Field.magicfield; return changed;}
    dragfrom=from;
    for(int i=0; i<6; i++){
      if ((getField().field[dragfrom+Field.around[i]] & Field.fieldexoccupied)==Field.fieldexoccupiedno) {getField().field[dragfrom+Field.around[i]] |= Field.fieldmarkdrag; changed=true;}
    }
    return changed;
  }

  // Performs a placement if possible
  public boolean Place(int to) {
    if (!canPlace(to)) {return false;}
    getField().field[to] |= Field.fieldoccupied | Field.fieldplayer;
    getField().field[to] ^= (1-playturn);
    dragcount=0;
    eventhandler.SusanGamePlacePerformed(playturn,to);
    FinishAction();
    return true;
  }

  // Checks if a placement is possible
  public boolean canPlace(int to) {
    if (status!=statusplaying) {return false;}
    return ((getField().field[to] & Field.fieldexoccupied)==Field.fieldexoccupiedno);
  }

  // Finishes a drag or a placement
  private void FinishAction () {
    if (dragcount==6) {
      status=statusfinished;
      eventhandler.SusanGameFinished(-1);
    } else {
      int winner=hasWinner(playturn);
      markWinner(winner);
      playturn=1-playturn;

      if (winner==-1) {
        eventhandler.SusanGamePrepareNextTurn(playturn);
      } else {
        status=statusfinished;
        eventhandler.SusanGameFinished(winner);
      }
    }
    eventhandler.SusanGameHistoryChanged();
  }

  // Check if someone has won the game
  private int hasWinner(int lastplayer) {
    int winner=-1;
    for (int i=0; i<121; i++) {
      if ((getField().field[i] & Field.fieldexoccupied)==Field.fieldexoccupiedyes) {
        int n=0;
        for (; n<6; n++) {
          if ((getField().field[i+Field.around[n]] & Field.fieldexoccupied)==Field.fieldexoccupiedno) {break;}
        }
        if (n==6) {
          if ((getField().field[i] & Field.fieldplayer)==lastplayer) {
            return 1-lastplayer;
          } else {
            winner=lastplayer;
          }
        }
      }
    }
    return winner;
  }

  // Marks the loosing stones with the field.fieldmarklost flag
  private void markWinner(int winner) {
    for (int i=0; i<121; i++) {
      getField().field[i] |= Field.fieldmarklost;
      getField().field[i] ^= Field.fieldmarklost;
      if ((getField().field[i] & Field.fieldexoccupied)==Field.fieldexoccupiedyes) {
        int n=0;
        for (; n<6; n++) {
          if ((getField().field[i+Field.around[n]] & Field.fieldexoccupied)==Field.fieldexoccupiedno) {break;}
        }
        if (n==6) {
          if ((getField().field[i] & Field.fieldplayer)!=winner) {getField().field[i] |= Field.fieldmarklost;}
        }
      }
    }
  }

  // ######################### COORDINATE TRANSFORM
  // SusanGame works internally with special coordinates to simplify and
  // minimize calculation. These methods for transformation between the
  // different systems.

  // Transforms the interal coordinates into a1-coordinates
  public static String toString(int coord) {
    char[] cc=new char[2];
    cc[0]=(char)(coord / 11);
    cc[1]=(char)(coord % 11);
    if (cc[0]>5) {cc[1]-=(cc[0]-5);}
    cc[0]+=96;
    cc[1]+=48;
    return new String(cc);
  }

  // Gets the row of a field.field
  public static char getRow(int coord) {
    char c=(char)(coord / 11 + 96);
    return c;
  }

  // Gets the column of a field.field
  public static int getColumn(int coord) {
    int row=coord / 11;
    int col=coord % 11;
    if (row>5) {col-=(row-5);}
    return col;
  }

  // Transforms the a1-coordinates into interal coordinates
  // Returns Field.magicfield if the a1-coordinates are invalid
  public static int getPosition(String t) {
    try {
    	int row=t.charAt(0)-96;
      int col=t.charAt(1)-48;
      if ((col<1) || (col>9)) {return Field.magicfield;}
      if ((row<1) || (row>9)) {return Field.magicfield;}
      if (row>5) {col+=(row-5);}
      return row*11+col;
    } catch (Exception e) {
      return Field.magicfield;
    }
  }

  // Transforms the row/col into interal coordinates
  // Returns Field.magicfield if the coordinates are invalid
  public static int getPosition(char rw, int col) {
    int row=rw-96;
    if ((col<1) || (col>9)) {return Field.magicfield;}
    if ((row<1) || (row>9)) {return Field.magicfield;}
    if (row>5) {col+=(row-5);}
    return row*11+col;
  }

  // ######################### GAME ANALYSE
  // The following methods are looking though the field.field on order to find and
  // mark special stones.

  // Find stones which are unbeatable at the current moment
  public boolean calcFortress (int player) {
    //int fieldoc=Field.fieldplayer | Field.fieldoccupied;
    int fieldexoccplme=Field.fieldexoccupiedyes | player;
    //int field.fieldpl=field.fieldoccupied | player;
    int fieldexoccplother=Field.fieldexoccupiedyes | (1-player);
    for (int i=0; i<121; i++) {
      if ((getField().field[i] & Field.fieldexists)!=0) {
        int n;
        getField().field[i] |= Field.fieldtemp;
        for (n=0; n<6; n++) {
          getField().field[i+Field.around[n]] |= Field.fieldtemp;
        }
        for (n=0; n<6; n++) {
          int m=i+Field.around[n];
          if (((getField().field[m] & Field.fieldexoccupied)==Field.fieldexoccupiedno) || ((getField().field[m] & Field.fieldexoccpl)==fieldexoccplother)) {
            int w=0;
            for (; w<6; w++) {
              if ((getField().field[m+Field.around[w]] & Field.fieldtemp)!=0) {
              } else if ((getField().field[m+Field.around[w]] & Field.fieldexists)==0) {
              } else if ((getField().field[m+Field.around[w]] & Field.fieldexoccpl)==fieldexoccplme) {
              //} else if ((field.field[m+Field.around[w]] & field.fieldoc)==field.fieldpl) {
              } else {
                break;
              }
            }
            if (w==6) {break;}
          }
        }
        getField().field[i]|=Field.fieldmarkfort;
        if (n==6) {getField().field[i]^=Field.fieldmarkfort;}
        getField().field[i] ^= Field.fieldtemp;
        for (n=0; n<6; n++) {
          getField().field[i+Field.around[n]] ^= Field.fieldtemp;
        }
      } else {
        getField().field[i]|=Field.fieldmarkfort;
        getField().field[i]^=Field.fieldmarkfort;
      }
    }
    return true;
  }

  // Delete all fortress marks
  public boolean hideFortress () {
    for (int i=0; i<121; i++) {
      getField().field[i]|=Field.fieldmarkfort;
      getField().field[i]^=Field.fieldmarkfort;
    }
    return true;
  }

  // Find own stones which are enclosed by 5 stones
  public boolean calcLoosers (int player) {
    int fieldoc=Field.fieldplayer | Field.fieldoccupied;
    int fieldpl=Field.fieldoccupied | player;
    for (int i=0; i<121; i++) {
      if ((getField().field[i] & fieldoc)==fieldpl) {
        int n;
        int free=0;
        for (n=0; n<6; n++) {
          int m=i+Field.around[n];
          if ((getField().field[m] & Field.fieldexoccupied)==Field.fieldexoccupiedno) {
            free++;
            if (free==2) {break;}
          }
        }
        getField().field[i]|=Field.fieldmarklooser;
        if (free!=1) {getField().field[i]^=Field.fieldmarklooser;}
      } else {
        getField().field[i]|=Field.fieldmarklooser;
        getField().field[i]^=Field.fieldmarklooser;
      }
    }
    return true;
  }

  // Delete all looser marks
  public boolean hideLoosers () {
    for (int i=0; i<121; i++) {
      getField().field[i]|=Field.fieldmarklooser;
      getField().field[i]^=Field.fieldmarklooser;
    }
    return true;
  }

  // Find opponent's stones which are enclosed by 5 stones
  public boolean calcVictims (int player) {
    int fieldoc=Field.fieldplayer | Field.fieldoccupied;
    int fieldpl=Field.fieldoccupied | (1-player);
    for (int i=0; i<121; i++) {
      if ((getField().field[i] & fieldoc)==fieldpl) {
        int n;
        int free=0;
        for (n=0; n<6; n++) {
          int m=i+Field.around[n];
          if ((getField().field[m] & Field.fieldexoccupied)==Field.fieldexoccupiedno) {
            free++;
            if (free==2) {break;}
          }
        }
        getField().field[i]|=Field.fieldmarkvictim;
        if (free!=1) {getField().field[i]^=Field.fieldmarkvictim;}
      } else {
        getField().field[i]|=Field.fieldmarkvictim;
        getField().field[i]^=Field.fieldmarkvictim;
      }
    }
    return true;
  }

  // Delete all looser marks
  public boolean hideVictims () {
    for (int i=0; i<121; i++) {
      getField().field[i]|=Field.fieldmarkvictim;
      getField().field[i]^=Field.fieldmarkvictim;
    }
    return true;
  }
  
  public void moveComputer() {
	  getComputer().move();
  }

  public Field getField() {
	  return field;
  }

  public Computer getComputer() {
	  return computer;
  }
  
  // Starts a game
  public void start() {
    eventhandler.SusanGamePrepareNextTurn(playturn);
    eventhandler.SusanGameHistoryChanged();
    status=statusplaying;
  }

  // Starts a game
  public void stop() {
    status=statusstopped;
  }
}
// ######################### END OF FILE
